#ifndef MLTOOL_LAYOUT_H
#define MLTOOL_LAYOUT_H

#include<algorithm>
#include"tags.h"

namespace math{

	inline size_t lower_element(size_t i, size_t j, size_t size_i, size_t size_j)
	{
		return (i * (i + 1)) / 2 + j;
	}


	inline size_t upper_element(size_t i, size_t j, size_t size_i, size_t size_j)
	{
		return (i * (2 * ::std::max(size_i, size_j) - i + 1)) / 2 + j - i;
	}

	struct lower
	{	
		typedef lower_tag triangular_category;

		static inline
			size_t storage_size(size_t size_i, size_t size_j)
		{
			size_t sz = ::std::max(size_i, size_j);
			return (sz + 1) * sz / 2;
		}

		static inline
			size_t element(size_t i, size_t j, size_t size_i, size_t size_j)
		{
			return lower_element(i, j, size_i, size_j);
		}

		static inline
			bool one(size_t i, size_t j)
		{
			return false;
		}

		static inline
			bool zero(size_t i, size_t j)
		{
			return i < j;
		}

		static inline
			bool other(size_t i, size_t j)
		{
			return i >= j;
		}
	};

	struct unit_lower
	{
		typedef unit_lower_tag triangular_category;

		static inline
			size_t storage_size(size_t size_i, size_t size_j)
		{
			if(size_i == 0 || size_j == 0)
				return 0;
			size_t sz = ::std::max(size_i, size_j);
			return (sz - 1) * sz / 2;
		}


		static inline
			size_t element(size_t i, size_t j, size_t size_i, size_t size_j)
		{
			return lower_element(i - 1, j, size_i - 1, size_j - 1);
		}

		static inline
			bool one(size_t i, size_t j)
		{
			return i == j;
		}

		static inline
			bool zero(size_t i, size_t j)
		{
			return i < j;
		}

		static inline
			bool other(size_t i, size_t j)
		{
			return i > j;
		}
	};

	struct strict_lower
	{
		typedef strict_lower_tag triangular_category;
		static inline
			size_t storage_size(size_t size_i, size_t size_j)
		{
			if(size_i == 0 || size_j == 0)
				return 0;
			size_t sz = std::max(size_i, size_j);
			return (sz - 1) * sz / 2;
		}


		static inline
			size_t element(size_t i, size_t j, size_t size_i, size_t size_j)
		{
			return lower_element(i - 1, j, size_i - 1, size_j - 1);
		}

		static inline
			bool one(size_t i, size_t j)
		{
			return false;
		}

		static inline
			bool zero(size_t i, size_t j) 
		{
			return i <= j;
		}

		static inline
			bool other(size_t i, size_t j)
		{
			return i > j;
		}
	};

	struct upper
	{
		typedef upper_tag triangular_category;
		static inline
			size_t storage_size(size_t size_i, size_t size_j)
		{
			size_t sz = ::std::max(size_i, size_j);
			return (sz + 1) * sz / 2;
		}

		static inline
			size_t element(size_t i, size_t j, size_t size_i, size_t size_j)
		{
			return upper_element(i,j,size_i,size_j);
		}

		static inline
			bool one(size_t i, size_t j)
		{
			return false;
		}

		static inline
			bool zero(size_t i, size_t j)
		{
			return i > j;
		}

		static inline
			bool other(size_t i, size_t j)
		{
			return i <= j;
		}
	};

	struct unit_upper
	{
		typedef unit_upper_tag triangular_category;
		static inline
			size_t storage_size(size_t size_i, size_t size_j)
		{
			if(size_i == 0 || size_j == 0)
				return 0;
			size_t sz = ::std::max(size_i, size_j);
			return (sz - 1) * sz / 2;
		}

		static inline
			size_t element(size_t i, size_t j, size_t size_i, size_t size_j)
		{
			return upper_element(i ,j - 1,size_i - 1,size_j - 1);
		}

		static inline
			bool one(size_t i, size_t j)
		{
			return i == j;
		}

		static inline
			bool zero(size_t i, size_t j)
		{
			return i > j;
		}

		static inline
			bool other(size_t i, size_t j)
		{
			return i < j;
		}
	};

	struct strict_upper
	{
		typedef strict_upper_tag triangular_category;
		static inline
			size_t storage_size(size_t size_i, size_t size_j)
		{
			if(size_i == 0 || size_j == 0)
				return 0;
			size_t sz = ::std::max(size_i, size_j);
			return (sz - 1) * sz / 2;
		}

		static inline
			size_t element(size_t i, size_t j, size_t size_i, size_t size_j)
		{
			return upper_element(i, j - 1, size_i - 1, size_j - 1);
		}

		static inline
			bool one(size_t i, size_t j)
		{
			return false;
		}

		static inline
			bool zero(size_t i, size_t j) 
		{
			return i >= j;
		}

		static inline
			bool other(size_t i, size_t j)
		{
			return i < j;
		}
	};
}

#endif



